/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFTracker
  \brief a track finder, consisting of DCs, tracking algorithm, and tracks
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-11-01
  \date 2021-11-08 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2021-11-01   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#ifndef ETFTracker_h
#define ETFTracker_h

#include <vector>
#include <map>
#include "ETFVDC.h"
#include "ETFYaml.h"

class TClonesArray;
class TObject;
class ETFVTrack;
class ETF3DTrack;
class ETFDCHit;
class ETFDetectorManager;
class ETFParaManager;
class ETFTimer;

using std::vector;
using std::map;

class ETFTracker{
public:
  ETFTracker(const char *name = "");
  /// \param dcs: dcs used in the tracking, t0,1: timing detectors
  ETFTracker(const char *name, const vector<string> &dcs, const string &t0 = "",
    const string &t1 = "");
  virtual ~ETFTracker();

  /// clear data for next event
  virtual void Initialize();
  virtual void Reset(){ Initialize(); }
  void SetMapped(bool opt, TrackType type = TrackType::kX);
  virtual void Register(); ///< register tracks to main TTree
  virtual void Map3D() = 0; ///< 3D tracking
  virtual void Map() = 0; ///< X tracking
  ///< if accept the layer or not in Map()
  virtual bool GoodLayer(ETFVDC *dc, DCType type, int x12) const;
  /// \param type: 0: X; 1: U; 2: V
  virtual void Map(TrackType type); ///< finding 2D tracks
  virtual void TrackMerge(); ///< merge 2D tracks to 3D ones
  /// remove nullptr in TClonesArray to output clones array - IMPORTANT
  virtual void Clean(TClonesArray *cIn, TClonesArray *cOut);
  /// incompatibility check of trk to every stored track, \retval true: trk accepted
  virtual bool Compare(ETFVTrack *trk, TrackType type);
  void SetDebug(bool opt = true){ fDebug = opt; }
  ///@{ visualization toolkit
  virtual void ConstructGraph(); ///< construct graphs for the X tracks
  virtual void Draw(); ///< draw the tracks
  virtual void Write(const char *name = ""); ///< write tracks to file
  ///@} visualization toolkit

  const char *GetName() const{ return fName.data(); }
  const vector<ETFVDC *> &GetDCArray() const{ return fDCs; }
  TClonesArray *GetTracks(TrackType type = TrackType::kX) const{ return fTracks[short(type)]; }
  TClonesArray *Get3DTracks() const{ return f3DTracks; }
  ETFVTrack *GetTrack(int i, TrackType type = TrackType::kX);
  ETF3DTrack *Get3DTrack(int i); ///< get the i-th 3D track
  int GetNofTracks(TrackType type = TrackType::kX) const;
  int GetNof3DTracks() const;
  bool HasMapped(TrackType type = TrackType::kX) const{ return fHasMapped[short(type)]; }
  const map<int, ETFDCHit *> &GetHits() const{ return fHits; }
  virtual void Print(); ///< print the tracks in the tracker
  double GetChiPD() const{ return fChiPD; }
  double GetD2THREPD() const{ return D2THREPD; }

  // ClassDefOverride(ETFTracker, 1);

protected:
  /// called by Map, to test if a hit combination is eligible
  /// \retval true: if the current track candidate is accepted; false: otherwise
  /// called by Map, to test if a hit combination is eligible
  virtual bool TestCombination(TrackType type) = 0; ///< \param type: track type
  /// incompatibility check betweeen newtrk and oldtrk, \retval true: trk accepted
  virtual bool Compare(ETFVTrack *newtrk, ETFVTrack *oldtrk, TrackType type);
  /// \retval false: if obtained drift time out of range
  virtual bool SetDriftDistance(ETFVTrack *trk);
  virtual void UpdateHitPos(); ///< update the hit position of the tracks in the tracker
  virtual void UpdateHitPos(ETFDetector *det); ///< update the hit position at det
  /// construct an object in the first empty slot in c, so as to avoid overwriting good slots
  virtual TObject *ConstructedAt(TClonesArray *c);
  ETFTimer *GetTimer(int i = 0) const; // return fTimer[i]

  string fName;
  bool fBroken; ///< the tracker is broken when dcs <= 1, or timing det is absent
  /// hit combination for a track - a temporary variable
  map<int, ETFDCHit *> fHits; //! ///< map<layerId, ETFDCHit *>
  vector<ETFVDC *> fDCs; //! ///< the DC array employed by the tracker for the tracking
  TClonesArray *fTracks[3]; //! ///< the fitted 2D tracks: [X,U,V], or [X,Y]
  TClonesArray *f3DTracks; //! ///< the fitted 3D tracks
  TClonesArray *fTracksOut[3]; //-> ///< output tracks, with nullptrs removed
  bool fHasMapped[3]; //! ///< if Map function has been called for the current event
  /// for recording map(TrackType) recursion depth, a temporary variable
  int fNLayer; //! ///< also is the sense wire layer the current map function is in
  ETFDetectorManager *fdm; //!
  ETFParaManager *fpm; //!
  const ETFYaml *fcfg;  //! ///< parameters to configure the tracking process
  ETFTimer *fTimer[3]; //! ///< timers for timing

  double D2THREPD; //! ///< threshold for D2 per dot, for tracking filtering
  double fChiPD; //! ///< threshold for chi per dot, for tracking filtering
  int fMultiThrePerL; //! ///< layers with multiplicity >= this value would be nullified
  bool fDebug;
};

#endif
